package com.bitbakery.plugin.scheme.lang.parser;

import static com.bitbakery.plugin.scheme.SchemeConstants.*;
import com.bitbakery.plugin.scheme.lang.lexer.SchemeTokenTypes;
import static com.bitbakery.plugin.scheme.lang.lexer.SchemeTokenTypes.*;
import static com.bitbakery.plugin.scheme.lang.psi.SchemeElementTypes.*;
import com.intellij.lang.PsiBuilder;
import com.intellij.psi.tree.IElementType;

public class SchemeStatementParser {
    public static void parseFunctionCall(PsiBuilder builder) {
        // TODO - Handle null, premature right paren, etc.
        if (builder.getTokenType() == LEFT_PAREN) {
            PsiBuilder.Marker marker = builder.mark();
            parseFunctionCall(builder); // TODO - BROKEN! WE HAVEN"T ADVANCED THE LEXER!!!
            marker.done(FUNCTION_CALL);
        }

        parseFunctionName(builder);
        parseFunctionArguments(builder);

        if (builder.getTokenType() != RIGHT_PAREN) {
            builder.error(ERROR_EXPECTED_RIGHT_PAREN);
        } else {
            // Set us to the first token following the function call
            advanceLexer(builder);
        }
    }

    protected static void parseFunctionName(PsiBuilder builder) {
        IElementType token = builder.getTokenType();
        if (token == SchemeTokenTypes.SYMBOL
                || SchemeTokenTypes.KEYWORDS.contains(token)) {
            PsiBuilder.Marker marker = builder.mark();
            advanceLexer(builder);
            marker.done(FUNCTION_NAME);
        } else {
            builder.error(ERROR_EXPECTED_FUNCTION_NAME);
        }
    }

    protected static void parseFunctionArguments(PsiBuilder builder) {
        if (builder.getTokenType() == RIGHT_PAREN) {
            return; // No arguments - just bail
        }

        PsiBuilder.Marker marker = builder.mark();
        while (builder.getTokenType() != RIGHT_PAREN && builder.getTokenType() != null) {
            parseFunctionArgument(builder);
        }
        marker.done(ARGUMENT_LIST);
    }

    private static void parseFunctionArgument(PsiBuilder builder) {
        PsiBuilder.Marker marker = builder.mark();
        if (builder.getTokenType() == LEFT_PAREN) {
            // TODO - This could also be an anonymous function definition!
            PsiBuilder.Marker argMarker = builder.mark();
            advanceLexer(builder);
            parseFunctionCall(builder);
            argMarker.done(FUNCTION_CALL);
        } else if (builder.getTokenType() == COMMA) {
            PsiBuilder.Marker argMarker = builder.mark();
            advanceLexer(builder);
            parseFunctionArgument(builder);
            argMarker.done(COMMA);
        } else if (builder.getTokenType() == COMMA_AT) {
            PsiBuilder.Marker argMarker = builder.mark();
            advanceLexer(builder);
            parseFunctionArgument(builder);
            argMarker.done(COMMA_AT);
        } else if (builder.getTokenType() != SchemeTokenTypes.SYMBOL
                && builder.getTokenType() != NUMERIC_LITERAL
                && builder.getTokenType() != STRING_LITERAL
                && builder.getTokenType() != NIL) {

            builder.error(ERROR_EXPECTED_SYMBOL_OR_ETC);
            advanceLexer(builder);
        } else {
            advanceLexer(builder);
        }
        marker.done(ARGUMENT);
    }

    private static void advanceLexer(PsiBuilder builder) {
        if (builder.getTokenType() != null) {
            builder.advanceLexer();
        }
    }
}
